package io.litchi.starter.jjwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.litchi.starter.jjwt.exception.ParseTokenFailException;
import io.litchi.starter.jjwt.properties.JjwtProperties;
import lombok.AllArgsConstructor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 *
 * jwt工具
 *
 * @author helang
 * @since 2025-09-19
 */

@AllArgsConstructor
public class JwtManager {
    private static final Logger log = LoggerFactory.getLogger(JwtManager.class);
    private JjwtProperties jjwtProperties;

    /**
     * 将字符串secretKey包装成SecretKey对象
     *
     * @return SecretKey
     */
    private SecretKey getSecretKey() {
        return Keys.hmacShaKeyFor(jjwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成token
     *
     * @param username    用户标识 一般id
     * @param extraClaims 扩展参数
     * @param expireTime  过期时间
     * @return token
     */
    public String createToken(String username, Map<String, Object> extraClaims, Long expireTime) {
        if (extraClaims == null) {
            extraClaims = new HashMap<>(2);
        }
        extraClaims.put("clintId", jjwtProperties.getClientId());
        return Jwts.builder()
                .claims(extraClaims)
                .subject(username)
                .issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + expireTime))
                .signWith(getSecretKey(), Jwts.SIG.HS256) // 指定签名算法
                .compact();
    }

    /**
     * 使用默认过期时间
     *
     * @param username    用户标识
     * @param extraClaims 扩展参数
     * @return token
     */
    public String createToken(String username, Map<String, Object> extraClaims) {
        return this.createToken(username, extraClaims, jjwtProperties.getExpiration());
    }

    /**
     * 解析token获取额外参数  过期token校验会抛出异常 这里手动处理异常 以便获取token中的参数
     *
     * @param token token
     * @return claims
     */
    public Claims extractAllClaims(String token) {
        try {
            // 正常解析（会验证 exp）
            return Jwts.parser()
                    .verifyWith(getSecretKey())
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();
        } catch (ExpiredJwtException e) {
            // Token 已过期，但你可以从异常中获取原始的 Claims
            return e.getClaims();
        } catch (JwtException e) {
            // 其他 JWT 异常（签名无效、格式错误等）

            log.error("Jwt exception", e);
            throw new ParseTokenFailException("token解析失败");
        }
    }


    /**
     * 解析token获取用户唯一标识
     *
     * @param token token
     * @return subject
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 获取指定参数 参数消费函数即可获取
     *
     * @param token         token
     * @param claimResolver 消费对象
     * @param <T>           获取类型
     * @return T
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimResolver) {
        final Claims claims = extractAllClaims(token);
        return claimResolver.apply(claims);
    }

    /**
     * token是否有效 true 有效 false 无效
     *
     * @param token token
     * @return boolean
     */
    public boolean validateToken(String token) {
        return Objects.nonNull(token) && extractExpiration(token).after(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

}